package scene.accel.bvh;

import java.util.Collections;
import java.util.List;

import raytracing.AccelerationStructure;
import raytracing.HitRecord;
import raytracing.Ray;
import scene.Scene;
import scene.shape.BoundingBox;
import scene.shape.Hittable;
import scene.shape.geometry.Primitive;

public class BVHNode implements Hittable, AccelerationStructure{
	
	protected Hittable left, right;
	protected BoundingBox bbox;
	
	public BVHNode(List<Primitive> A, int AXIS){
		int N = A.size();
		if(N == 0){
			left = null;
			right = null;
		}
		else if(N == 1){
			left = A.get(0);
			right = null;
			bbox = A.get(0).getBoundingBox();
		}
		else if(N == 2){
			left = A.get(0);
			right = A.get(1);
			bbox = BoundingBox.merge(A.get(0).getBoundingBox(), A.get(1).getBoundingBox());
		}
		else{
//			float m = BoundingBox.computeBoundingBox(A).getCenter().get(AXIS);
//			List<Primitive> left = new ArrayList<Primitive>();
//			List<Primitive> right = new ArrayList<Primitive>();
//			partition(A,m,AXIS,left,right);
			sortPrimitiveList(A, AXIS);
			List<Primitive> left = A.subList(0, N/2), right = A.subList(N/2, N);
			this.left = new BVHNode(left, (AXIS + 1) % 3);
			this.right = new BVHNode(right, (AXIS + 1) % 3);
			bbox = BoundingBox.merge(BoundingBox.computeBoundingBox(left), BoundingBox.computeBoundingBox(right));
		}
	}
	
	private void sortPrimitiveList(List<Primitive> A, int AXIS){
		PrimitiveAxisComparator comp = new PrimitiveAxisComparator(AXIS);
		Collections.sort(A, comp);
	}
	
	@SuppressWarnings("unused")
	private void partition(List<Primitive> a, float m, int AXIS, List<Primitive> left, List<Primitive> right) {
		for(Primitive p : a){
			int compare = compareToPivot(p, AXIS, m);
			if(compare == 1)
				right.add(p);
			else if(compare == -1)
				left.add(p);
			else
				left.add(p);
		}
	}
	
	private int compareToPivot(Primitive p, int axis, float pivot){
    	float c = p.getCenter().get(axis);
    	if(c > pivot)
    		return 1;
    	if(c < pivot)
    		return -1;
    	return 0;
    }

	public BoundingBox getBoundingBox(){
		return bbox;
	}
	
	public HitRecord hit(Ray r, float mint, float maxt){
		if(bbox.intersects(r)){
			HitRecord lrec = null, rrec = null, rec = null;
			if(left != null)
				lrec = left.hit(r, mint, maxt);
			if(right != null)
				rrec = right.hit(r,mint, maxt);
			if(lrec != null && rrec != null && lrec.hasHit() && rrec.hasHit()){
				if(lrec.getT() < rrec.getT())
					rec = lrec;
				else
					rec = rrec;
				return rec;
			}
			else if(lrec != null && lrec.hasHit())
				return lrec;
			else if(rrec != null && rrec.hasHit())
				return rrec;
			else
				return HitRecord.NOHIT;
		}
		return HitRecord.NOHIT;
	}

	@Override
	public void build(Scene scene) {
		
	}

	@Override
	public HitRecord intersects(Ray r, float mint, float maxt) {
		return hit(r, mint, maxt);
	}
}
